﻿namespace zijian666.Data.SqlElements;
public static class SqlOperator
{
    /// <summary>
    /// 等于操作符
    /// </summary>
    public class EqualAttribute() : SqlOperatorAttribute("{0:##} {1:=}", 2);

    /// <summary>
    /// 不等于操作符
    /// </summary>
    public class NotEqualAttribute() : SqlOperatorAttribute("{0:##} {1:<>}", 2);

    /// <summary>
    /// 大于操作符
    /// </summary>
    public class GreaterThanAttribute() : SqlOperatorAttribute("{0:##} > {1}", 2);

    /// <summary>
    /// 大于等于操作符
    /// </summary>
    public class GreaterThanOrEqualAttribute() : SqlOperatorAttribute("{0:##} >= {1}", 2);

    /// <summary>
    /// 小于操作符
    /// </summary>
    public class LessThanAttribute() : SqlOperatorAttribute("{0:##} < {1}", 2);

    /// <summary>
    /// 小于等于操作符
    /// </summary>
    public class LessThanOrEqualAttribute() : SqlOperatorAttribute("{0:##} <= {1}", 2);

    /// <summary>
    /// Like 操作符
    /// </summary>
    public class LikeAttribute() : SqlOperatorAttribute("{0:##} Like {1}", 2);

    /// <summary>
    /// Not Like 操作符
    /// </summary>
    public class NotLikeAttribute() : SqlOperatorAttribute("{0:##} NOT Like {1}", 2);

    /// <summary>
    /// Between 操作符
    /// </summary>
    public class BetweenAttribute() : SqlOperatorAttribute("{0:##} BETWEEN {1} AND {2}", 3);

    /// <summary>
    /// Not Between 操作符
    /// </summary>
    public class NotBetweenAttribute() : SqlOperatorAttribute("{0:##} NOT BETWEEN {1} AND {2}", 3);

    /// <summary>
    /// Is Null 操作符
    /// </summary>
    public class IsNullAttribute() : SqlOperatorAttribute("{0:##} IS NULL", 1);

    /// <summary>
    /// Is Not Null 操作符
    /// </summary>
    public class IsNotNullAttribute() : SqlOperatorAttribute("{0:##} IS NOT NULL", 1);

    /// <summary>
    /// In 操作符
    /// </summary>
    public class InAttribute() : SqlOperatorAttribute("{0:##} IN ({1})", 2);

    /// <summary>
    /// Not In 操作符
    /// </summary>
    public class NotInAttribute() : SqlOperatorAttribute("{0:##} NOT IN ({1})", 2);

    /// <summary>
    /// Exists 操作符
    /// </summary>
    public class ExistsAttribute() : SqlOperatorAttribute("EXISTS ({1})", 2);

    /// <summary>
    /// Not Exists 操作符
    /// </summary>
    public class NotExistsAttribute() : SqlOperatorAttribute("NOT EXISTS({1})", 2);


    /// <summary>
    /// OR 操作符
    /// </summary>
    public class OrAttribute() : ConcatSqlOperatorAttribute(false);


    /// <summary>
    /// AND 操作符
    /// </summary>
    public class AndAttribute() : ConcatSqlOperatorAttribute(true);

    #region static

    /// <summary>
    /// 等于操作符实例
    /// </summary>
    public static readonly EqualAttribute Equal = new();

    /// <summary>
    /// 不等于操作符实例
    /// </summary>
    public static readonly NotEqualAttribute NotEqual = new();

    /// <summary>
    /// 大于操作符实例
    /// </summary>
    public static readonly GreaterThanAttribute GreaterThan = new();

    /// <summary>
    /// 大于等于操作符实例
    /// </summary>
    public static readonly GreaterThanOrEqualAttribute GreaterThanOrEqual = new();

    /// <summary>
    /// 小于操作符实例
    /// </summary>
    public static readonly LessThanAttribute LessThan = new();

    /// <summary>
    /// 小于等于操作符实例
    /// </summary>
    public static readonly LessThanOrEqualAttribute LessThanOrEqual = new();

    /// <summary>
    /// Like 操作符实例
    /// </summary>
    public static readonly LikeAttribute Like = new();

    /// <summary>
    /// Not Like 操作符实例
    /// </summary>
    public static readonly NotLikeAttribute NotLike = new();

    /// <summary>
    /// Between 操作符实例
    /// </summary>
    public static readonly BetweenAttribute Between = new();

    /// <summary>
    /// Not Between 操作符实例
    /// </summary>
    public static readonly NotBetweenAttribute NotBetween = new();

    /// <summary>
    /// Is Null 操作符实例
    /// </summary>
    public static readonly IsNullAttribute IsNull = new();

    /// <summary>
    /// Is Not Null 操作符实例
    /// </summary>
    public static readonly IsNotNullAttribute IsNotNull = new();

    /// <summary>
    /// In 操作符实例
    /// </summary>
    public static readonly InAttribute In = new();

    /// <summary>
    /// Not In 操作符实例
    /// </summary>
    public static readonly NotInAttribute NotIn = new();

    /// <summary>
    /// Exists 操作符实例
    /// </summary>
    public static readonly ExistsAttribute Exists = new();

    /// <summary>
    /// Not Exists 操作符实例
    /// </summary>
    public static readonly NotExistsAttribute NotExists = new();

    /// <summary>
    /// True 操作符实例
    /// </summary>
    public static readonly SqlOperatorAttribute True = new("1=1", 0);

    /// <summary>
    /// False 操作符实例
    /// </summary>
    public static readonly SqlOperatorAttribute False = new("1=0", 0);

    /// <summary>
    /// And 操作符实例
    /// </summary>
    public static readonly SqlOperatorAttribute And = new("({1:& AND })", 2);

    /// <summary>
    /// Or 操作符实例
    /// </summary>
    public static readonly SqlOperatorAttribute Or = new("({1:& OR })", 2);

    #endregion
}

public interface ISqlOperatorAttribute
{
    SqlCondition Build(SqlTable table, PropertyInfo property, object value);
}
/// <summary>
/// 表示条件属性，用于标记属性或字段的查询条件。
/// </summary>
/// <param name="expr">条件表达式。</param>
/// <param name="parametersLength">参数长度。</param>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
[DebuggerDisplay("{DebuggerView,nq}")]
public class SqlOperatorAttribute(string expr, int parametersLength) : Attribute, ISqlOperatorAttribute
{
    /// <summary>
    /// 获取条件表达式。
    /// </summary>
    public string FormatExpression { get; } = expr;

    /// <summary>
    /// 获取参数长度。
    /// </summary>
    public int ParametersLength { get; } = parametersLength;

    /// <summary>
    /// 列名
    /// </summary>
    public string? Column { get; set; }

    /// <summary>
    /// 获取或设置子查询的列名数组。
    /// </summary>
    public string[]? SubSelect { get; set; }

    private string DebuggerView => FormatExpression;

    public SqlCondition Build(SqlTable table, PropertyInfo property, object value)
    {
        if (value is ISqlSearcher sub)
        {
            if (SubSelect?.Length is null or 0)
            {
                throw new NotSupportedException($"子查询必须设置 {nameof(SubSelect)} 属性");
            }
            var subQuery = sub.Build();
            if (subQuery.IsEmpty || subQuery.Where.IsEmpty)
            {
                return default;
            }
            value = subQuery with { Select = new SqlSelect() { Columns = SubSelect.Select(c => new SqlColumn(subQuery.Table, c)).ToArray() } };
        }

        var array = ParametersLength switch
        {
            < 2 => default,
            2 => new object[] { value },
            _ => ToArray(value, $"{property.DeclaringType?.FullName}.{property.Name}"),
        };
        var column = new SqlColumn(table, Column ?? property.Name);
        var condition = new SqlCondition(column, this, array);

        return condition;
        static object[] ToArray(object value, string name)
        {
            if (value is object[] objects)
            {
                return objects;
            }
            if (value is IEnumerable<object> array)
            {
                return array.ToArray();
            }

            if (value is IEnumerable enumerable)
            {
                return enumerable.Cast<object>().ToArray();
            }
            throw new NotSupportedException($"{name} 无法转换为数组, value = {value}");
        }
    }
}


[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)]
[DebuggerDisplay("{DebuggerView,nq}")]
public class ConcatSqlOperatorAttribute(bool isAnd) : Attribute, ISqlOperatorAttribute
{
    public bool IsAnd { get; } = isAnd;

    public SqlCondition Build(SqlTable table, PropertyInfo property, object value)
    {
        if (value is not IEnumerable<ISqlSearcher> searchers)
        {
            return default;
        }
        var conditions = new List<SqlCondition>();
        foreach (var searcher in searchers)
        {
            var subQuery = searcher.Build();
            if (subQuery.IsEmpty || subQuery.Where.IsEmpty)
            {
                continue;
            }
            conditions.Add(subQuery.Where.Condition);
        }

        return IsAnd
            ? SqlCondition.And(conditions)
            : SqlCondition.Or(conditions);
    }

    private string DebuggerView => $"IsAnd: {IsAnd}";
}